package logic;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import bean.Arma;
import bean.Grupo;
import bean.Muerte;
import bean.juego.Juego;
import bean.juego.JuegoCTF;
import bean.jugador.Jugador;
import java.util.logging.Level;
import java.util.logging.Logger;
import util.GrupoComparator;
import util.MyUtils;
import util.Pair;
import logic.cache.CacheCaller;
import logic.cache.CacheService;

public class Filtro {

    private static final Random random = new Random();
    private static final Logger logger = Logger.getLogger(Filtro.class.getName());

    public static int cantidadMuertesJugadorAJugador(final Jugador victimario, final Jugador victima) {
        CacheCaller<Integer> caller = new CacheCaller<Integer>() {

            @Override
            public String getKey() {
                return Filtro.class.getCanonicalName() + "cantidadMuertesJugadorAJugador(" + victimario + "," + victima + ")";
            }

            @Override
            public Integer callMethod() {
                int count = 0;
                for (Muerte muerte : victimario.getMuertesHechas()) {
                    if (victima == muerte.getVictima()) {
                        count++;
                    }
                }

                return count;
            }
        };

        return CacheService.getFromCache(caller);
    }

    public static int cantidadMuertesJugadorAJugadorConArma(final Jugador victimario, final Jugador victima, final Arma arma) {

        CacheCaller<Integer> caller = new CacheCaller<Integer>() {

            @Override
            public String getKey() {
                return Filtro.class.getCanonicalName() + ".cantidadMuertesJugadorAJugadorConArma(" + victimario + "," + victima + "," + arma + ")";
            }

            @Override
            public Integer callMethod() {
                int count = 0;
                for (Muerte muerte : victimario.getMuertesHechas()) {
                    if (victima == muerte.getVictima() && muerte.getArma() == arma) {
                        count++;
                    }
                }
                return count;
            }
        };

        return CacheService.getFromCache(caller);
    }

    public static int cantidadMuertesJugadorConArma(final Jugador victimario, final Arma arma) {

        CacheCaller<Integer> caller = new CacheCaller<Integer>() {

            @Override
            public String getKey() {
                return Filtro.class.getCanonicalName() + ".cantidadMuertesJugadorConArma(" + victimario + "," + arma + ")";
            }

            @Override
            public Integer callMethod() {
                int count = 0;
                for (Muerte muerte : victimario.getMuertesHechas()) {
                    if (muerte.getArma() == arma) {
                        count++;
                    }
                }
                return count;
            }
        };

        return CacheService.getFromCache(caller);
    }

    public static int cantidadMuertesJugadorPorArma(final Jugador victima, final Arma arma) {

        CacheCaller<Integer> caller = new CacheCaller<Integer>() {

            @Override
            public String getKey() {
                return Filtro.class.getCanonicalName() + ".cantidadMuertesJugadorPorArma(" + victima + "," + arma + ")";
            }

            @Override
            public Integer callMethod() {
                int count = 0;
                for (Muerte muerte : victima.getMuertesRecibidas()) {
                    if (muerte.getArma() == arma) {
                        count++;
                    }
                }

                return count;
            }
        };

        return CacheService.getFromCache(caller);
    }

    public static List<Pair<Jugador, Integer>> muertesJugadorPorJugador(final Jugador jugador) {

        CacheCaller<List<Pair<Jugador, Integer>>> caller = new CacheCaller<List<Pair<Jugador, Integer>>>() {

            @Override
            public String getKey() {
                return Filtro.class.getCanonicalName() + ".muertesJugadorPorJugador(" + jugador + ")";
            }

            @Override
            public List<Pair<Jugador, Integer>> callMethod() {
                Map<Jugador, Integer> map = new HashMap<Jugador, Integer>();

                for (Muerte muerte : jugador.getMuertesHechas()) {
                    Jugador victima = muerte.getVictima();
                    if (map.containsKey(victima)) {
                        map.put(victima, map.get(victima) + 1);
                    } else {
                        map.put(victima, 1);
                    }
                }

                List<Pair<Jugador, Integer>> lista = new ArrayList<Pair<Jugador, Integer>>();

                for (Jugador victima : map.keySet()) {
                    lista.add(new Pair<Jugador, Integer>(victima, map.get(victima)));
                }

                return Collections.unmodifiableList(lista);
            }
        };

        return CacheService.getFromCache(caller);
    }

    public static List<Pair<Arma, Integer>> muertesJugadorPorArma(final Jugador jugador) {

        CacheCaller<List<Pair<Arma, Integer>>> caller = new CacheCaller<List<Pair<Arma, Integer>>>() {

            @Override
            public String getKey() {
                return Filtro.class.getCanonicalName() + ".muertesJugadorPorArma(" + jugador + ")";
            }

            @Override
            public List<Pair<Arma, Integer>> callMethod() {
                Map<Arma, Integer> map = new HashMap<Arma, Integer>();

                for (Muerte muerte : jugador.getMuertesHechas()) {
                    Arma arma = muerte.getArma();
                    if (map.containsKey(arma)) {
                        map.put(arma, map.get(arma) + 1);
                    } else {
                        map.put(arma, 1);
                    }
                }

                List<Pair<Arma, Integer>> lista = new ArrayList<Pair<Arma, Integer>>();

                for (Arma arma : map.keySet()) {
                    lista.add(new Pair<Arma, Integer>(arma, map.get(arma)));
                }

                return Collections.unmodifiableList(lista);
            }
        };

        return CacheService.getFromCache(caller);
    }

    public static List<Grupo> getGruposJugadores(final int cuantosTomo) {

        CacheCaller<List<Grupo>> caller = new CacheCaller<List<Grupo>>() {

            @Override
            public String getKey() {
                return Filtro.class.getCanonicalName() + ".getGruposJugadores(" + cuantosTomo + ")";
            }

            @Override
            public List<Grupo> callMethod() {
                Set<Set<Jugador>> combinaciones = Filtro.getCombinacionesJugadores(cuantosTomo);

                List<Grupo> retorno = new ArrayList<Grupo>();
                for (Set<Jugador> set : combinaciones) {
                    Grupo grupo = new Grupo(set);
                    if (grupo.getCantidadJuegosJugados() > 0) {
                        retorno.add(grupo);
                    }
                }

                Collections.sort(retorno, GrupoComparator.getInstancia());
                return Collections.unmodifiableList(retorno);
            }
        };

        return CacheService.getFromCache(caller);
    }

    public static List<Grupo> getGruposJugadores(final List<Jugador> jugadores, final List<Juego> juegos) {

        CacheCaller<List<Grupo>> caller = new CacheCaller<List<Grupo>>() {

            @Override
            public String getKey() {
                return Filtro.class.getCanonicalName() + ".getGruposJugadores(" + jugadores + "," + juegos + ")";
            }

            @Override
            public List<Grupo> callMethod() {
                Set<Set<Jugador>> allCombinaciones = new HashSet<Set<Jugador>>();

                for (int i = 1; i <= jugadores.size(); i++) {
                    Set<Set<Jugador>> combinaciones = Filtro.getCombinacionesJugadores(jugadores, i);
                    allCombinaciones.addAll(combinaciones);
                }

                List<Grupo> retorno = new ArrayList<Grupo>();
                for (Set<Jugador> set : allCombinaciones) {
                    Grupo grupo = new Grupo(set, juegos);
                    if (grupo.getCantidadJuegosJugados() > 0) {
                        retorno.add(grupo);
                    }
                }

                Collections.sort(retorno, GrupoComparator.getInstancia());
                return Collections.unmodifiableList(retorno);
            }
        };

        return CacheService.getFromCache(caller);
    }

    private static Set<Set<Jugador>> getCombinacionesJugadores(int cuantosTomo) {
        return getCombinacionesJugadores(Buscador.getJugadores(), cuantosTomo);
    }

    private static Set<Set<Jugador>> getCombinacionesJugadores(final List<Jugador> jugadores, final int cuantosTomo) {

        CacheCaller<Set<Set<Jugador>>> caller = new CacheCaller<Set<Set<Jugador>>>() {

            @Override
            public String getKey() {
                return Filtro.class.getCanonicalName() + ".getCombinacionesJugadores(" + jugadores + "," + cuantosTomo + ")";
            }

            @Override
            public Set<Set<Jugador>> callMethod() {
                int cantJugadores = jugadores.size();

                List<Integer> cuantosHay = new ArrayList<Integer>();
                for (int i = 0; i < cantJugadores; i++) {
                    cuantosHay.add(i);
                }

                Set<Set<Integer>> comb = getCombinaciones(cuantosHay, cuantosTomo);
                Set<Set<Jugador>> retorno = new HashSet<Set<Jugador>>();

                for (Set<Integer> is : comb) {
                    Set<Jugador> lista = new HashSet<Jugador>();
                    for (int i : is) {
                        lista.add(jugadores.get(i));
                    }
                    retorno.add(lista);
                }

                return Collections.unmodifiableSet(retorno);
            }
        };

        return CacheService.getFromCache(caller);
    }

    private static Set<Set<Integer>> getCombinaciones(final List<Integer> cuantosHay, final int cuantosTomo) {

        CacheCaller<Set<Set<Integer>>> caller = new CacheCaller<Set<Set<Integer>>>() {

            @Override
            public String getKey() {
                return Filtro.class.getCanonicalName() + ".getCombinaciones(" + cuantosHay + "," + cuantosTomo + ")";
            }

            @Override
            public Set<Set<Integer>> callMethod() {
                if (cuantosHay.size() < cuantosTomo) {
                    throw new IllegalArgumentException("cuantosHay: " + cuantosHay.size() + ". cuantosTomo" + cuantosTomo);
                }

                Set<Set<Integer>> retorno = new HashSet<Set<Integer>>();

                if (cuantosTomo == 0 || cuantosHay.isEmpty()) {
                    return Collections.unmodifiableSet(retorno);
                } else if (cuantosTomo == 1) {
                    for (Integer integer : cuantosHay) {
                        Set<Integer> set = new HashSet<Integer>();
                        set.add(integer);
                        retorno.add(Collections.unmodifiableSet(set));
                    }

                    return Collections.unmodifiableSet(retorno);

                } else {
                    for (Integer elijo1 : cuantosHay) {
                        List<Integer> removido1 = new ArrayList<Integer>(cuantosHay);
                        removido1.remove(elijo1);

                        for (Set<Integer> smaller : Filtro.getCombinaciones(removido1, cuantosTomo - 1)) {
                            Set<Integer> set = new HashSet<Integer>(smaller);
                            set.add(elijo1);
                            retorno.add(Collections.unmodifiableSet(set));
                        }
                    }

                    return Collections.unmodifiableSet(retorno);
                }
            }
        };

        // TODO ver porque no funca con el cache
        return CacheService.getFromCache(caller);
    }

    public static String getNombreJugadoresCSV() {

        CacheCaller<String> caller = new CacheCaller<String>() {

            @Override
            public String getKey() {
                return Filtro.class.getCanonicalName() + ".getNombreJugadoresCSV";
            }

            @Override
            public String callMethod() {
                StringBuilder sb = new StringBuilder();

                Jugador bot = Buscador.getBot();
                for (Jugador jugador : Buscador.getJugadores()) {
                    if (jugador.equals(bot)) {
                        continue;
                    }

                    if (sb.length() > 0) {
                        sb.append(',');
                    }

                    sb.append(jugador.getDatos().getNombre());
                }

                return sb.toString();
            }
        };

        return CacheService.getFromCache(caller);
    }

    public static List<Jugador> getJugadoresCSV(final String csv) {

        CacheCaller<List<Jugador>> caller = new CacheCaller<List<Jugador>>() {

            @Override
            public String getKey() {
                return Filtro.class.getCanonicalName() + ".getJugadoresCSV(" + csv + ")";
            }

            @Override
            public List<Jugador> callMethod() {
                List<Jugador> retorno = new ArrayList<Jugador>();

                String[] split = csv.split(",");

                for (String string : split) {
                    retorno.add(Buscador.jugadorPorApodo(string));
                }

                return Collections.unmodifiableList(retorno);
            }
        };

        return CacheService.getFromCache(caller);
    }

    public static Map<Jugador, Integer> getHistoricoMVP() {

        CacheCaller<Map<Jugador, Integer>> caller = new CacheCaller<Map<Jugador, Integer>>() {

            @Override
            public String getKey() {
                return Filtro.class.getCanonicalName() + ".getHistoricoMVP";
            }

            @Override
            public Map<Jugador, Integer> callMethod() {
                Map<Jugador, Integer> map = new HashMap<Jugador, Integer>();

                List<Jugador> jugadores = Buscador.getJugadores();

                for (Jugador jugador : jugadores) {
                    map.put(jugador, 0);
                }

                List<Juego> juegos = Buscador.getListaJuegos();

                for (Juego juego : juegos) {

                    List<Jugador> mvps = juego.getMVPHumano();

                    for (Jugador mvp : mvps) {
                        Integer i = map.get(mvp);
                        if (i != null) {
                            map.put(mvp, i + 1);
                        }
                    }
                }

                return Collections.unmodifiableMap(map);
            }
        };

        return CacheService.getFromCache(caller);
    }

    public static String getScreenName(Jugador jugador) {
        String titulo = jugador.getDatosF().getTitulo();
        String nombre = jugador.getDatos().getNombre();
        if (MyUtils.isNull(titulo)) {
            return nombre;
        } else {
            return new String[]{titulo, nombre}[random.nextInt(2)];
        }
    }
}
